package jMiiEditor.mii.checker;

import jMiiEditor.mii.Mii;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import utils.BinStuffs;

public class MiiCheckerProposalMiiAction
    extends MiiCheckerProposalAbstractAction
        //implements MiiCheckerProposalAction
{
    private static final long serialVersionUID = 1L;
//    private String      message;
    /** @serial */
    private String      miiMethodName;
    /** @serial */
    private Object      miiMethodParam;
    /** @serial */
    private Class<?>    miiMethodParamType;
    //private int         miiMethodParamRadix;

    public MiiCheckerProposalMiiAction(
            String  message,
            boolean isSelected,
            String  miiMethodName,
            boolean miiMethodParam
            )
    {
        super(message,isSelected);

        this.miiMethodName = miiMethodName;
        this.miiMethodParam = miiMethodParam;
        this.miiMethodParamType = Boolean.TYPE;
    }

    public MiiCheckerProposalMiiAction(
            String  message,
            boolean isSelected,
            String  miiMethodName,
            int     miiMethodParam//,
            //int     miiMethodParamRadix
            )
    {
        super(message,isSelected);

        this.miiMethodName = miiMethodName;
        this.miiMethodParam = miiMethodParam;
        this.miiMethodParamType = Integer.TYPE;
        //this.miiMethodParamRadix = miiMethodParamRadix;
    }

//    public MiiCheckerProposalMiiAction(
//            String  message,
//            String  miiMethodName,
//            int     miiMethodParam
//            )
//    {
//        this( message, miiMethodName, miiMethodParam, 10 );
//    }

    public MiiCheckerProposalMiiAction(
            String  message,
            boolean isSelected,
            String  miiMethodName,
            byte[]  miiMethodParam
            )
    {
        super(message,isSelected);

        this.miiMethodName = miiMethodName;
        this.miiMethodParam = miiMethodParam;
        this.miiMethodParamType = byte[].class;
    }

//    @Override
//    public String getMessage()
//    {
//        return message;
//    }

    @Override
    public String getMessageAction()
    {
        StringBuilder sb = new StringBuilder();

        sb.append( "Mii." );
        sb.append( this.miiMethodName );
        sb.append( "( " );

        if( this.miiMethodParam instanceof String ) {
            sb.append( '"' );
            sb.append( getParameterString() );
            sb.append( '"' );
        }
        else if( this.miiMethodParam instanceof byte[] ) {
            sb.append( getParameterString() );
        }
        else {
            sb.append( getParameterString() );
        }

        sb.append( " )" );

        return sb.toString();
    }

    @Override
    public String getParameterString()
    {
        if( this.miiMethodParam instanceof byte[] ) {
            return BinStuffs.toHexString( (byte[])this.miiMethodParam );
            //byte[] b = byte[].class.cast( this.miiMethodParam );
            //return String.format( "%1$02X%2$02X%3$02X%4$02X", b[0], b[1], b[2], b[3] );
        }
        else {
            return miiMethodParam.toString();
        }
    }

    @Override
    public boolean setParameterString( String newValue )
    {
        if( this.miiMethodParamType == Boolean.TYPE ) {
            boolean v = Boolean.parseBoolean( newValue );
            this.miiMethodParam = v;
            return true;
        }
        else if( this.miiMethodParamType == Integer.TYPE ) {
            int v = Integer.parseInt( newValue/*, miiMethodParamRadix*/ );
            this.miiMethodParam = v;
            return true;
        }
        else if( this.miiMethodParamType == String.class ) {
            this.miiMethodParam = newValue;
            return true;
        }
        else if( this.miiMethodParamType == byte[].class ) {
            byte[] bytes;

            try {
                bytes = BinStuffs.hexStringToByteArray( newValue );
            }
            catch( Exception badValue ) {
                return false;
            }

            if( bytes.length == 4 ) {
                this.miiMethodParam = bytes;
                return true;
            }
        }

        return false;
    }

    @Override
    public void applyOn( MiiCheckerResult aMiiCheckerResult )
            throws MiiCheckerActionException
    {
        applyOn( aMiiCheckerResult.getMii() );
        aMiiCheckerResult.addAction( getMessageAction() );
    }

    private void applyOn( Mii mii )
        throws MiiCheckerActionException
    {
        if( this.miiMethodName ==  null ) { // Mii is not define
            throw new MiiCheckerActionException( "Mii is not define." );
        }

        Method m;

        try {
            //m = Mii.class.getMethod( miiMethodName, miiMethodParam.getClass() );
            Class<?>[] paramsType = new Class<?>[1];
            paramsType[0] = miiMethodParamType;

            m = Mii.class.getMethod( miiMethodName, paramsType );
        }
        catch( SecurityException e ) {
            throw buildMiiCheckerActionException( e );
        }
        catch( NoSuchMethodException e ) {
            throw buildMiiCheckerActionException(
                    "Method not found: " + miiMethodName + "(" + miiMethodParam.getClass() + ")",
                    e
                    );
        }

        Object[] args = new Object[1];
        args[0] = this.miiMethodParam;

        try {
            m.invoke( mii, args );
        }
        catch( SecurityException e ) {
            throw buildMiiCheckerActionException( e );
        }
        catch( IllegalArgumentException e ) {
            throw buildMiiCheckerActionException( e );
        }
        catch( IllegalAccessException e ) {
            throw buildMiiCheckerActionException( e );
        }
        catch( InvocationTargetException e ) {
            throw buildMiiCheckerActionException( e );
        }
    }

    public MiiCheckerActionException buildMiiCheckerActionException(
            Throwable cause
            )
    {
        return buildMiiCheckerActionException(null, cause);
    }

    public MiiCheckerActionException buildMiiCheckerActionException(
            String    message,
            Throwable cause
            )
    {
        StringBuilder sb = new StringBuilder();

        sb.append( "Can't apply: " );
        sb.append( getMessageAction() );

        if( message != null ) {
            sb.append( " - " );
            sb.append( message );
        }

        return new MiiCheckerActionException( sb.toString(), cause );
    }

    /* (non-Javadoc)
     * @see java.lang.Object#toString()
     */
    @Override
    public String toString()
    {
        StringBuilder builder = new StringBuilder();
        builder.append( "MiiCheckerResultMiiAction [getMessage()=" );
        builder.append( getMessage() );
        builder.append( ", getMessageAction()=" );
        builder.append( getMessageAction() );
        builder.append( "]" );
        return builder.toString();
    }

}
